package com.gitee.wsl.time

import com.gitee.wsl.ext.bitcode.umod
import kotlinx.datetime.LocalTime
import kotlinx.datetime.UtcOffset
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlin.time.ExperimentalTime


/**
 * Represents a span of time, with [milliseconds] precision.
 *
 * It is a value class wrapping [Double] instead of [Long] to work on JavaScript without allocations.
 */
typealias TimeSpan = Duration

operator fun Duration.unaryPlus(): Duration = this

val Duration.milliseconds: Double get() = this.inWholeNanoseconds.toDouble() / 1_000_000.0

/** Returns the total number of [milliseconds] as a [Long] */
val Duration.millisecondsLong: Long get() = this.milliseconds.toLong()
/** Returns the total number of [milliseconds] as an [Int] */
val Duration.millisecondsInt: Int get() = this.milliseconds.toInt()

fun TimeSpan(milliseconds: Double): Duration = milliseconds.milliseconds

operator fun Duration.plus(other: MonthSpan): DateTimeSpan = DateTimeSpan(other, this)
operator fun Duration.plus(other: DateTimeSpan): DateTimeSpan = DateTimeSpan(other.monthSpan, other.timeSpan + this)

operator fun Duration.minus(other: MonthSpan): DateTimeSpan = this + (-other)
operator fun Duration.minus(other: DateTimeSpan): DateTimeSpan = this + (-other)

operator fun Duration.times(scale: Float): Duration = TimeSpan((this.milliseconds * scale))
operator fun Duration.div(scale: Float): Duration = TimeSpan(this.milliseconds / scale)

infix fun Duration.divFloat(other: Duration): Float = (this.milliseconds / other.milliseconds).toFloat()
operator fun Duration.rem(other: Duration): Duration = (this.milliseconds % other.milliseconds).milliseconds
infix fun Duration.umod(other: Duration): Duration = (this.milliseconds umod other.milliseconds).milliseconds

/** Return true if [Duration.NIL] */
val Duration.isNil: Boolean get() = this == DURATION_NIL

fun TimeSpan.toLocalTime() = LocalTime.fromMillisecondOfDay(this.millisecondsInt)

/**
 * Convert this duration to [LocalTime]. If this duration is larger than 24 hours, clock will wrap around.
 */
val Duration.asLocalTime: LocalTime get() = LocalTime.fromNanosecondOfDay(inWholeNanoseconds)


@OptIn(ExperimentalTime::class)
fun UtcOffset.toDuration() = this.totalSeconds.seconds


// fun LocalTime.Companion.parseOrNull(isoString: String): LocalTime? =
//    runCatching { parse(isoString) }.getOrNull()